using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

//This assumes that all circular actors' positions are determined by their center, 
//and all rectangular actors' positions are determined by top left corner

namespace ChildrenOfWar
{
    class COW_Enemy : COW_Person
    {
        Vector2 EnemyPos;
        int sightRadius;
        double sightWidthAngle = Math.PI / 4;  // Angle from center of sight to edge
        double enemyAngle;

        public COW_Enemy()   //Hehehe. Hehehe. Public enemy.
        {

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<COW_Kid> KidsInArea()
        {
            // return list of Kids in Vector3: <kidX, kidY, 1>  1 denotes still seen, 0 denotes hidden 
            // if no kids, return null
            List<COW_Kid> testReturn = new List<COW_Kid>();
            testReturn.Add(new COW_Kid(25, 45, true)); // should be seen         //*** I need to check over this math again *****//
            testReturn.Add(new COW_Kid(35, 35, true));// should be seen
            testReturn.Add(new COW_Kid(35, 51, true));// shouldn't be seen
            testReturn.Add(new COW_Kid(41, 45, true));// shouldn't be seen
            testReturn.Add(new COW_Kid(80, 60, true));// should be seen
            testReturn.Add(new COW_Kid(40, 30, true));// should be seen
            testReturn.Add(new COW_Kid(70, 30, true));// should not be seen
            testReturn.Add(new COW_Kid(30, 10, true));// should be seen
            testReturn.Add(new COW_Kid(70, 5, true)); // should be seen
            return testReturn;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<COW_ObstacleC> CircleBlocksSeen(List<COW_ObstacleC> obstList)
        {
            //return array of Blockers in Vec3: <blockX, blockY, blockRadius>
            //if no blockers, return null
            List<COW_ObstacleC> testReturn = new List<COW_ObstacleC>();
            foreach (COW_ObstacleC thisObst in obstList)
            {
                if(isInFOV(thisObst.position))
                    testReturn.Add(thisObst);
                //DO THE THINGS
                //TBI: Distance to line formula in 2D  - To find circular obsts with centers outside, but edges inside FOV
            }
            return testReturn;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<COW_ObstacleSQ> RectBlocksSeen(List<COW_ObstacleSQ> obstList)
        {
            //return array of Blocker in Vec4 : <blockX, blockY, blockWidth, blockHeight>
            //if no blockers, return null
            List<COW_ObstacleSQ> testReturn = new List<COW_ObstacleSQ>();
            foreach(COW_ObstacleSQ thisObst in obstList)
            {
                foreach (Vector2 thisVec in thisObst.Corners)
                {
                    if (isInFOV(thisVec))
                    {
                        testReturn.Add(thisObst);
                        break;
                    }
                }
            }
            return testReturn;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<COW_Kid> CanSeeKids(List<COW_ObstacleC> obstListC, List<COW_ObstacleSQ> obstListSQ)
        {
            COW_Kid[] Kids = KidsInArea().ToArray();
            if (Kids == null)
                return null;
            else
            {
                COW_ObstacleC[] CircBlocks = CircleBlocksSeen(obstListC).ToArray();
                COW_ObstacleSQ[] RectBlocks = RectBlocksSeen(obstListSQ).ToArray();
                foreach (COW_Kid thisKid in Kids)
                {
                    if (CircBlocks != null)
                    {
                        foreach (COW_ObstacleC thisBlock in CircBlocks)
                        {   /*  Process:
                             *  1) find angle between the line (enemy to center of circle) and the other line (enemy to angular edge of the circle, from enemy's POV)
                             *  2) find angle between the line (enemy to center of circle) and the other line (enemy to kid)
                             *  3) If angle 1 < angle 2, kid is seen
                             *  4) If angle 1 > angle 2, check kid's distance vs. distance to edge of circle. If kid dist < distance to circle, kid seen
                             *  5) Otherwise, kid is hidden
                             */
                            double BlockRadius = thisBlock.radius;
                            Vector2 enemyToKid = new Vector2(thisKid.position.X - EnemyPos.X, thisKid.position.Y - EnemyPos.Y);
                            Vector2 enemyToBlock = new Vector2(thisBlock.position.X - EnemyPos.X, thisBlock.position.Y - EnemyPos.Y);
                            double distEnemytoBlockCenter = enemyToBlock.Length();
                            double distEnemytoBlockEdge = Math.Sqrt(Math.Pow(distEnemytoBlockCenter, 2) - Math.Pow(BlockRadius, 2)); // Holy crap trigonometry
                            double phi = Math.Asin(BlockRadius / distEnemytoBlockCenter);
                            if (distEnemytoBlockEdge < enemyToKid.Length())
                            {
                                if (Math.Acos(Vector2.Dot(Vector2.Normalize(enemyToKid), Vector2.Normalize(enemyToBlock))) < phi)
                                {
                                    thisKid.setSeen(false); // kid no longer seen
                                    break;
                                }
                            }
                        }
                    }
                    if (RectBlocks != null && thisKid.isSeen)
                    {
                        foreach (COW_ObstacleSQ thisBlock2 in RectBlocks)
                        {
                            /* Process similar to the Circle process
                             * But a f***ton more complex. Yay, rectangular + angular math!
                             * really, there's no way to describe this in words. Just ask me, I have it written out in math-ish.
                             */
                            double omega, tau, psi, lambda; /* Omega = angle from X Axis to CCW corner (once translated to 1st quadrant)
                                                             * Tau = angle from X axis to close corner (once translated to 1st quadrant)
                                                             * Psi = angle from X axis to CW corner (once translated to 1st quadrant)
                                                             * lambda = angle from X axis to kid (once translated to 1st quadrant)
                                                             */
                            Vector2 EnemytoRectPos, EnemytoCloseCorner, EnemytoCWCorner, EnemytoCCWCorner, EnemytoKid;
                            EnemytoKid = new Vector2(Math.Abs(EnemyPos.X - thisKid.position.X), Math.Abs(EnemyPos.Y - thisKid.position.Y));
                            EnemytoRectPos = new Vector2(thisBlock2.position.X - EnemyPos.X, thisBlock2.position.Y - EnemyPos.Y);

                            // If there are bugs with this code, they'll probably be here
                            Vector2 EnemytoRectCenter = new Vector2(Math.Abs(thisBlock2.position.X + (thisBlock2.dimensions.X / 2)), Math.Abs(thisBlock2.position.Y + (thisBlock2.dimensions.Y / 2)));

                            EnemytoCloseCorner = new Vector2(EnemytoRectCenter.X - (thisBlock2.dimensions.X/ 2), EnemytoRectCenter.Y - (thisBlock2.dimensions.Y / 2));
                            EnemytoCWCorner = new Vector2(EnemytoRectCenter.X + (thisBlock2.dimensions.X / 2), EnemytoRectCenter.Y - (thisBlock2.dimensions.Y / 2));
                            EnemytoCCWCorner = new Vector2(EnemytoRectCenter.X - (thisBlock2.dimensions.X / 2), EnemytoRectCenter.Y + (thisBlock2.dimensions.Y / 2));

                            // All the following code is based on 1st quadrant trig, so its correctness is dependent on my translation code above.
                            omega = Math.Atan(EnemytoCCWCorner.Y / EnemytoCCWCorner.X);
                            tau = Math.Atan(EnemytoCloseCorner.Y / EnemytoCloseCorner.X);
                            psi = Math.Atan(EnemytoCWCorner.Y / EnemytoCWCorner.X);
                            lambda = Math.Atan(EnemytoKid.Y / EnemytoKid.X);

                            if (lambda <= tau && lambda > psi)
                            {
                                if (EnemytoKid.Length() > (EnemytoCloseCorner.Y / Math.Sin(lambda)))
                                {
                                    thisKid.setSeen(false);
                                    break;
                                }
                            }
                            else if (lambda > tau && lambda < omega)
                            {
                                if (EnemytoKid.Length() > (EnemytoCloseCorner.X / Math.Cos(lambda)))
                                {
                                    thisKid.setSeen(false);
                                    break;
                                }
                            }
                        }
                    }
                }

                List<COW_Kid> KidsList = new List<COW_Kid>();
                foreach (COW_Kid thisKid2 in Kids)
                {
                    if (thisKid2.isSeen)
                    {
                        KidsList.Add(thisKid2);
                        Console.WriteLine("Kid seen at" + thisKid2.position.X + ", " + thisKid2.position.Y);
                    }
                }
                return KidsList;
            }
        }

        public bool isInFOV(Vector2 pointToSee)
        {
            Vector2 sightVector = pointToSee - EnemyPos;
            double distToPoint = sightVector.Length();
            sightVector.Normalize();
            double angleEnemytoPoint = Math.Asin((float)sightVector.Y);
            if(Math.Abs(angleEnemytoPoint - enemyAngle) > sightWidthAngle)
                return false;
            else{
                if(base.ObstacleOnLine(EnemyPos, pointToSee) == null && distToPoint < sightRadius)
                    return true;
                else
                    return false;
            }
        }

        /// <summary>
        /// If the enemy sees any Kids, it stops what it's doing and goes after them. Otherwise, it moves as a normal Person.
        /// </summary>
        public void Move(List<COW_ObstacleC> obstListC, List<COW_ObstacleSQ> obstListSQ)
        {
            List<COW_Kid> kidsList = CanSeeKids(obstListC, obstListSQ);
            if (kidsList.Count != 0)
            {
                base.clearPath();
                base.addPathNode(kidsList[0].position);
            }
            base.Move();
        }
    }

}